package com.yang.spring.core.bean.definition.builder;

import com.yang.spring.core.annotations.Bean;
import com.yang.spring.core.annotations.Qualifier;
import com.yang.spring.core.bean.definition.BeanDefinition;
import com.yang.spring.core.bean.enums.BeanDefinitionExtendsEnum;
import com.yang.spring.core.bean.enums.BeanTypeEnum;
import com.yang.spring.core.utils.StringUtils;

import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class ConfigurationBeanDefinitionBuilder extends AbstractBeanDefinitionBuilder {
    @Override
    public List<BeanDefinition> buildBeanDefinition(Class clazz) {
        BeanDefinition beanDefinition = new BeanDefinition();
        beanDefinition.setBeanClassType(clazz);
        beanDefinition.setBeanName(
                firstCharLowerCase(clazz.getName().substring(clazz.getName().lastIndexOf(".") + 1))
        );
        richBeanDefinitionByAnnotation(beanDefinition, clazz);

        Map<String, BeanDefinition> declaredFieldName2BeanDefinitionMap = new HashMap<>();
        declaredFieldName2BeanDefinitionMap.putAll(getDependencyBeanName2BeanDefinitionMapByField(beanDefinition));
        declaredFieldName2BeanDefinitionMap.putAll(getDependencyBeanName2BeanDefinitionMapByMethod(beanDefinition));

        declaredFieldName2BeanDefinitionMap.forEach((dependencyBeanName, dependencyBeanDefinition) -> {
            beanDefinition.putDependencyBeanDefinition(dependencyBeanName, dependencyBeanDefinition);
        });

        List<BeanDefinition> beanDefinitionList = new ArrayList<>();
        beanDefinitionList.add(beanDefinition);

        beanDefinition.getDependencyBeanDefinitionList()
                .stream()
                .filter(dependencyBeanDefinition -> dependencyBeanDefinition.getBeanType() == BeanTypeEnum.BEAN)
                .forEach(dependencyBeanDefinition -> {
                    beanDefinitionList.add(dependencyBeanDefinition);
                });
        return beanDefinitionList;
    }

    @Override
    protected Map<String, ? extends BeanDefinition> getDependencyBeanName2BeanDefinitionMapByMethod(BeanDefinition beanDefinition) {
        Class clazz = beanDefinition.getBeanClassType();
        Map<String, BeanDefinition> declaredFieldName2BeanDefinitionMap = new HashMap<>();

        Method[] declaredMethods = clazz.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            BeanDefinition dependencyBeanDefinition = getDependencyBeanDefinitionByMethod(declaredMethod);
            if (dependencyBeanDefinition == null) {
                continue;
            }
            declaredFieldName2BeanDefinitionMap.put(dependencyBeanDefinition.getBeanName(), dependencyBeanDefinition);
            richDependencyBeanDefinitionByAnnotation(dependencyBeanDefinition, beanDefinition, declaredMethod);
        }
        return declaredFieldName2BeanDefinitionMap;
    }

    @Override
    protected BeanDefinition getDependencyBeanDefinitionByMethod(Method method) {
        Bean bean = method.getAnnotation(Bean.class);
        if (bean == null) {
            return null;
        }
        String dependencyBeanName = firstCharLowerCase(method.getName());
        Class<?> dependencyBeanType = method.getReturnType();
        BeanDefinition dependenecyBeanDefinition = new BeanDefinition();
        dependenecyBeanDefinition.setBeanClassType(dependencyBeanType);
        dependenecyBeanDefinition.setBeanName(dependencyBeanName);
        return dependenecyBeanDefinition;
    }

    @Override
    protected void richBeanDefinitionByAnnotation(BeanDefinition beanDefinition, Class clazz) {
        beanDefinition.setBeanType(BeanTypeEnum.CONFIGURATION);
    }

    @Override
    protected void richDependencyBeanDefinitionByAnnotation(BeanDefinition dependencyBeanDefinition,
                                                            BeanDefinition beanDefinition, Method method) {
        Bean bean = method.getAnnotation(Bean.class);
        if (bean == null) {
            return;
        }
        if (StringUtils.isNotEmpty(bean.value())) {
            dependencyBeanDefinition.setBeanName(bean.value());
        }
        dependencyBeanDefinition.setBeanType(BeanTypeEnum.BEAN);
        dependencyBeanDefinition.putExtend(BeanDefinitionExtendsEnum.CONFIGURATION_BEAN_DEFINITION.getValue(), beanDefinition);
        dependencyBeanDefinition.putExtend(BeanDefinitionExtendsEnum.BEAN_METHOD.getValue(), method);
        Class<?>[] parameterTypes = method.getParameterTypes();
        Parameter[] parameters = method.getParameters();
        if (parameterTypes.length > 0) {
            for (int i = 0; i < parameterTypes.length; i++) {
                Class<?> parameterType = parameterTypes[i];
                String parameterName = parameters[i].getName();
                BeanDefinition dependencyDependencyBeanDefinition = new BeanDefinition();
                dependencyDependencyBeanDefinition.setBeanClassType(parameterType);
                dependencyDependencyBeanDefinition.setBeanName(parameterName);
                dependencyDependencyBeanDefinition.setBeanType(BeanTypeEnum.BEAN);
                Qualifier quailfierAnnotation = parameters[i].getAnnotation(Qualifier.class);
                if (quailfierAnnotation != null && StringUtils.isNotEmpty(quailfierAnnotation.value())) {
                    dependencyDependencyBeanDefinition.setBeanName(quailfierAnnotation.value());
                }
                dependencyBeanDefinition.putDependencyBeanDefinition(parameterName, dependencyDependencyBeanDefinition);
            }
        }
    }

    @Override
    protected void richDependencyBeanDefinitionByAnnotation(BeanDefinition dependencyBeanDefinition,
                                                            BeanDefinition beanDefinition, Field field) {
    }
}
